# !/usr/bin/env python3
# -*- coding: utf-8 -*-

import socket
import xml.etree.ElementTree as EleTree
from typing import List

from typing import List, Dict, Union


def host_to_net(value: int) -> str:
    # value = socket.htonl(value)
    # value = hex(value)[2:]
    value = '0x{:08X}'.format(value)
    return value


def discover_topo(topo_xml, node_type_set):
    # topo_set每一个表项的形式：
    # {'node_id': node_id,
    # 'port_id': port_id}
    topo_set = []

    tree = EleTree.parse(topo_xml)
    src_node_set = tree.iter("src_node")
    src_port_set = tree.iter("src_port")
    dst_node_set = tree.iter('dst_node')
    src_node_set = list(src_node_set)
    dst_node_set = list(dst_node_set)
    src_port_set = list(src_port_set)
    for src_node, src_port, dst_node in \
            zip(src_node_set, src_port_set, dst_node_set):

        topo_set.append({
            'src_node': int(src_node.text),
            'dst_node': int(dst_node.text),
            'port': int(src_port.text)
        })

    topo_set.sort(key=lambda x: (x['src_node'],
                                 x['port']))

    node_set = []
    for link in topo_set:
        if link['src_node'] not in node_set:
            node_set.append(link['src_node'])
    topo_set_tmp = topo_set
    topo = []
    for node in node_set:
        topo.append({'src_node': node,
                     'port': [],
                     # EDGE SW表示边缘交换节点，ES表示终端节点，NORMAL SW表示普通交换节点
                     'es_or_not': ''})

    for link_tmp in topo_set_tmp:
        src_node_tmp = link_tmp['src_node']
        port_tmp = link_tmp['port']

        for node in topo:
            if node['src_node'] == src_node_tmp:
                node['port'].append(port_tmp)

    for node_type in node_type_set:
        for node in topo:
            if node['src_node'] == node_type['node_id']:
                if node_type['node_type'] == 0:
                    node['es_or_not'] = 'SW'   # 交换机
                elif node_type['node_type'] == 1:
                    node['es_or_not'] = 'NIC'  # 网卡
                else:
                    node['es_or_not'] = 'OTHER'  # 传感器，测试仪

    return topo


def init_xml(conf_xml):
    root = EleTree.Element('network_plan_cfg')
    tree = EleTree.ElementTree(root)
    tree.write(conf_xml,
               encoding='utf-8',
               xml_declaration=True)
    return


def update_xml(conf_xml,
               addr_and_value_set: List[Dict[str, Union[str, List[str]]]],
               link: dict) -> None:
    tree = EleTree.parse(conf_xml)
    root = tree.getroot()

    node = EleTree.Element("node")
    node_child = EleTree.SubElement(node, "hcp_mid")
    node_id = link['src_node']
    node_child.text = str(node_id)

    # 将“地址-值”对写入xml文件
    for item in addr_and_value_set:
        node_child = EleTree.SubElement(node, "entry")
        node_child_addr = EleTree.SubElement(node_child, "addr")
        node_child_addr.text = item['addr']
        # print(type(item['addr']))
        value_set = item['value']
        # value_set的类型必须是列表
        if type(value_set) != list:
            print("error in tsn_builder_assembler\\tsn_builder_lib.py. "
                  "Check the type of value_set.")
            exit(0)
        for value in value_set:
            # print(type(value))
            node_child_value = EleTree.SubElement(node_child, 'data')
            node_child_value.text = value

    root.append(node)
    tree = EleTree.ElementTree(root)
    pretty_xml(root, '\t', '\n')  # 执行美化方法
    tree.write(conf_xml,
               encoding='utf-8',
               xml_declaration=True)


# element为传进来的Element类，参数indent用于缩进，newline用于换行
def pretty_xml(element, indent, newline, level=0):
    if element is not None:  # 判断element是否有子元素
        # print(element.text)
        if (element.text is None) or element.text.isspace():  # 如果element的text没有内容
            element.text = newline + indent * (level + 1)
        else:
            element.text = element.text.strip()
    temp = list(element)  # 将element转成list
    for sub_element in temp:
        # 如果不是list的最后一个元素，说明下一个行是同级别元素的起始，缩进应一致
        if temp.index(sub_element) < (len(temp) - 1):
            sub_element.tail = newline + indent * (level + 1)
        else:  # 如果是list的最后一个元素， 说明下一行是母元素的结束，缩进应该少一个
            sub_element.tail = newline + indent * level
        # 对子元素进行递归操作
        pretty_xml(sub_element, indent, newline, level=level + 1)
